home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************\
- *
- * Apple Macintosh Developer Technical Support
- *
- * Main program file for the ColorReset application
- *
- * Program: ColorReset
- * File: WindowPositioner.c
- *
- * by: Forrest Tanaka
- *
- * Copyright © 1988-1992 Apple Computer, Inc.
- * All rights reserved.
- *
- \******************************************************************************/
-
-
- /******************************************************************************\
- * Header Files
- \******************************************************************************/
-
- #ifndef THINK_C
- #include <Memory.h>
- #include <Script.h>
- #endif
-
- #include <GestaltEqu.h>
- #include "WindowPositioner.h"
-
-
- /******************************************************************************\
- * Constants
- \******************************************************************************/
-
- #define kAlertFactor 3 /* Denom of fraction of screen area above alert */
-
- /* Collision rectangle specifications */
- #define kCollInitH 0 /* Initial offset of collision rect from base rect */
- #define kCollInitV 0 /* Initial offset of collision rect from base rect */
- #define kCollWidth 16 /* Width of collision rectangle in pixels */
- #define kCollHeight 16 /* Height of collision rectangle in pixels */
-
- /* Staggered-window wrapping options */
- #define kNotWrapped 0 /* Collision testing hasn’t wrapped yet */
- #define kHorzWrapped 1 /* Collision testing wrapped horizontally */
- #define kVertWrapped 2 /* Collision testing wrapped vertically */
-
- /* Coordinates of temporary off-screen window for calculating bias */
- #define kOffScreenTop -32008 /* Top coordinate of off-screen window */
- #define kOffScreenLeft -32008 /* Left coordinate of off-screen window */
- #define kOffScreenBottom -32000 /* Bottom coordinate of off-screen window */
- #define kOffScreenRight -32000 /* Right coordinate of off-screen window */
-
- #ifndef THINK_C
- #define topLeft(r) (*((Point *) &(r).top))
- #define botRight(r) (*((Point *) &(r).bottom))
- #define screenBits qd.screenBits
- #endif
-
-
- /******************************************************************************\
- * Prototypes
- \******************************************************************************/
-
- void StaggerScreenRect(
- Rect *baseRect,
- Rect *modWindowRect,
- short hBias,
- short vBias);
-
- GDHandle GetWindowGDevice(
- WindowPtr theWindow);
-
- void GetGlobalWindowRect(
- WindowPtr theWindow,
- Rect *globalRect);
-
-
- /******************************************************************************\
- * Public: PositionScreenRect
- *
- * PositionScreenRect concerns itself with two rectangles: the base rectangle and
- * the window rectangle. The window rectangle is simply the rectangle of the
- * window as passed in the windowRect parameter. The base rectangle is what the
- * window rectangle should be positioned on. If the screenOption specifies that
- * the window rectangle should be positioned based on a screen, then the base
- * rectangle is the global rectangle of the screen that the window should be
- * displayed on. If the screenOption specifies that the window rectangle should
- * be positioned relative to the position of another window, then the base
- * rectangle is the global coordinates of the portRect of that window.
- *
- * Two screen options (specified by the screenOption parameter) specify that a
- * window should be based on one screen or another. Multiple screens are only
- * supported on Color QuickDraw machines, so PositionScreenRect starts off by
- * eliminating the parentScreenPos option on black & white QuickDraw machines by
- * changing the kParentScreenPos option to the kMainScreenPos option. Some
- * companies offer multiple screens for black & white QuickDraw machines, but
- * there’s no standard way to get at the rectangles of those other screens.
- *
- * The base rectangle is calculated first. If the screen option is
- * kMainScreenPos, then the base rectangle can be copied from the screenBits.
- * bounds QuickDraw global. If the screen option is kParentPos, then the base
- * rectangle is a copy of the portRect of the parent window (specified by the
- * parentWindow parameter) offset so that it has global coordinates. If the
- * screen option is kParentScreenPos, then the base rectangle is the rectangle of
- * the screen that the parent window has most of its area on. If the screen
- * option isn’t any of these, then the point (0,0) is returned and nothing more
- * is done.
- *
- * After the base rectangle is calculated, then the position of the window on
- * that base rectangle is calculated. If the position option is kCenterPos, then
- * the window rectangle is centered right smack in the middle of the base
- * rectangle. If the position option is kAlertPos, then the window rectangle is
- * centered horizontally, but it’s higher than the center vertically. The amount
- * higher that it should be is specified by the kAlertFactor constant. This
- * constant specifies the proportion of the vertical screen space aside from the
- * space taken up by the window should appear below the window. For now,
- * kAlertFactor is 3, which means that there should be three times more screen
- * space below the window than above.
- \******************************************************************************/
-
- void PositionScreenRect(
- Rect *windowRect, /* Rectangle to center */
- short screenOption, /* Options for screen to center against */
- short positionOption, /* Centering options */
- WindowPtr parentWindow, /* Pointer to parent window, if any */
- short hBias, /* portRect.left-strucRgn.rgnBBox.left */
- short vBias) /* portRect.top-strucRgn.rgnBBox.top */
- {
- long qdVersion; /* Version of QuickDraw in use */
- Rect baseRect; /* Rectangle to center against */
- GDHandle maxGDevice; /* GDevice containing most of parentWindow */
- short vCenterFactor; /* ÷ left-over screen by this to get v coord */
- Point upperLeft; /* Returns position of portRect.topLeft */
- Boolean goodScreenOption; /* True if screen option is valid */
- Boolean hasCQD; /* True if Color QuickDraw is available */
-
- /* Determine whether Color QuickDraw is available or not */
- (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
- hasCQD = qdVersion >= gestalt8BitQD;
-
- /* If want parent window’s screen but no CQD, then assume main screen */
- if (screenOption == kParentScreenPos && !hasCQD)
- screenOption = kMainScreenPos;
-
- /* If parent window is nil but it’s needed, then assume main screen */
- if ((screenOption == kParentPos || screenOption == kParentScreenPos) &&
- parentWindow == nil)
- screenOption = kMainScreenPos;
-
- /* Assume the specified screen option is good */
- goodScreenOption = true;
-
- /* Find the base rectangle */
- if (screenOption == kMainScreenPos)
- {
- /* Base rectangle is the main screen */
- baseRect = screenBits.bounds;
- baseRect.top += GetMBarHeight();
- }
- else if (screenOption == kParentPos)
- /* Base rectangle is the parent window’s portRect */
- GetGlobalWindowRect( parentWindow, /*<*/&baseRect );
- else if (screenOption == kParentScreenPos)
- {
- /* Base rectangle is screen containing most of parent window */
- maxGDevice = GetWindowGDevice( parentWindow );
- baseRect = (**maxGDevice).gdRect;
-
- /* If window’s GDevice is main screen’s, then take menu bar out */
- if (maxGDevice == GetMainDevice())
- baseRect.top += GetMBarHeight();
- }
- else
- {
- /* Invalid screen option */
- goodScreenOption = false;
- }
-
- /* Calculate a proper location for the window if screen option is valid */
- if (goodScreenOption)
- {
- if (positionOption == kStaggerPos)
- {
- StaggerScreenRect( &baseRect, /*◊*/windowRect, hBias, vBias );
- if (windowRect->right > baseRect.right - (kCollWidth / 2))
- windowRect->right = baseRect.right - (kCollWidth / 2);
- if (windowRect->bottom > baseRect.bottom - (kCollHeight / 2))
- windowRect->bottom = baseRect.bottom - (kCollHeight / 2);
- }
- else if (positionOption == kCenterPos || positionOption == kAlertPos)
- {
- /* Find amount to divide vertical screen area left over */
- if (positionOption == kCenterPos)
- vCenterFactor = 2;
- else if (positionOption == kAlertPos)
- vCenterFactor = kAlertFactor;
-
- /* Calc left and top coordinates of destination rectangle */
- upperLeft.h = (((baseRect.right - baseRect.left) - (windowRect->
- right - windowRect->left)) >> 1) + baseRect.left;
- upperLeft.v = (((baseRect.bottom - baseRect.top) - (windowRect->
- bottom - windowRect->top)) / vCenterFactor) + baseRect.top;
-
- /* Offset the window rectangle to upperLeft */
- OffsetRect( /*◊*/windowRect, upperLeft.h - windowRect->left,
- upperLeft.v - windowRect->top );
- }
- }
- }
-
-
- /******************************************************************************\
- * Private: StaggerScreenRect - Find staggered position for a window
- *
- * This routine returns a point that, if applied to the top-left corner of the
- * rectangle specified by windowRect, puts windowRect into staggered position
- * within the rectangle specified by baseRect. baseRect, windowRect, and the
- * returned point are all assumed to be in global (screen) coordinates.
- *
- * “Staggered position” means that an attempt is made to position windowRect such
- * that its top-left corner isn’t close to the top-left corner of an existing
- * window’s frame. To do this, a “collision rectangle” located near the top-left
- * corner of baseRect is created and placed into the collRect local variable.
- * The window list is searched to see if the top-left corner of any window frames
- * are in the collision rectangle, which would constitute a collision.
- * Initially, no collisions are tolerated. If even one colliding window is
- * found, then the collision rectangle is moved down and to the right and the
- * window list is searched for collisions again. If a collision is again found,
- * then the collision rectangle is moved down and to the right again and so
- * forth.
- *
- * A working copy of windowRect (StaggerScreenRect doesn’t modify windowRect)
- * in the local variable, workWindowRect, follows collRect such that it’s top-
- * left corner is at the center of collRect. As collRect is offset after a
- * collision is found, workWindowRect is offset by the same amount.
- *
- * If any part of workWindowRect falls outside of baseRect, then collRect and
- * workWindowRect wrap around to the side of baseRect opposite to the side that
- * workWindowRect fell off of, and collision-testing resumes there. This
- * wrapping is only allowed in one direction though. For example, if
- * workWindowRect falls off of the right edge of baseRect, then it and collRect
- * wrap around to the left side of baseRect at the same vertical position. If
- * the bottom edge of workWindowRect subsequently falls off of the bottom edge of
- * baseRect, then it won’t wrap around to the top because horizontally wrapping
- * has already happened. Instead, collRect and workWindowRect are set back into
- * their initial positions at the top-left corner of baseRect. If instead,
- * workWindowRect first falls off of the bottom edge of baseRect, then it and
- * collRect wrap around to the top of baseRect at the same horizontal position.
- * If the right edge of workWindowRect subsequently falls off of the right edge
- * of baseRect, then it won’t wrap around to the left because vertical wrapping
- * has already happened. Instead, collRect and workWindowRect are again set back
- * into their initial positions at the top-left corner of baseRect.
- *
- * Of course, setting collRect and workWindowRect back to their initial positions
- * guarantees a collision. So, StaggerScreenRect becomes more tolerant of
- * collisions. After finding a collision at every attempt, it now allows one
- * collision before moving on to the next position. If every position had more
- * than one collision, then StaggerScreenRect becomes even more tolerant and
- * allows up to two collisions. This process continues until a suitable position
- * for workWindowRect is found.
- *
- * In all truth, workWindowRect isn’t an EXACT copy of windowRect. The value of
- * the hBias parameter is added to both horizontal coordinates and the value of
- * vBias is added to both vertical coordinates. hBias should hold the number of
- * pixels between the top coordinate of the portRect of the window being
- * positioned and the top coordinate of the window’s frame. Similarly, vBias
- * should hold the number of pixels between the left coorindate of the portRect
- * and the left cooordinate of the window’s frame.
- \******************************************************************************/
-
- static void StaggerScreenRect(
- Rect *baseRect, /* Rectangle within which to stagger window rect */
- Rect *modWindowRect, /* Port rectangle to be staggered within baseRect */
- short hBias, /* portRect.left-strucRgn.rgnBBox.left */
- short vBias) /* portRect.top-strucRgn.rgnBBox.top */
- {
- WindowPtr testWindow; /* Window being tested to see if it collides */
- Rect testWindowRect; /* testWindow’s window frame rectangle */
- Rect collRect; /* Rectangle in which to test for collisions */
- Rect initCollRect; /* Initial collRect at upper left of baseRect */
- Rect workWindowRect; /* Working copy of modWindowRect */
- short windowRectWidth; /* Width of modWindowRect */
- short windowRectHeight; /* Height of modWindowRect */
- short collCount; /* # collisions found so far within collRect */
- short maxCollCount; /* Maximum allowed collisions within collRect */
- short wrapStatus; /* Tells how collRect has wrapped, if at all */
- Boolean foundSlot; /* True if found good slot for modWindowRect */
-
- /* Set up the initial collision rectangle at offset from baseRect */
- initCollRect.top = baseRect->top + kCollInitV;
- initCollRect.left = baseRect->left + kCollInitH;
- initCollRect.bottom = initCollRect.top + kCollHeight;
- initCollRect.right = initCollRect.left + kCollWidth;
-
- /* Set up the working destination rectangle */
- windowRectWidth = modWindowRect->right - modWindowRect->left;
- windowRectHeight = modWindowRect->bottom - modWindowRect->top;
- workWindowRect.top = initCollRect.top + vBias + kCollHeight / 2;
- workWindowRect.left = initCollRect.left + hBias + kCollWidth / 2;
- workWindowRect.bottom = workWindowRect.top + windowRectHeight;
- workWindowRect.right = workWindowRect.left + windowRectWidth;
-
- /* Set up initial conditions for the search */
- collRect = initCollRect;
- maxCollCount = 0;
- wrapStatus = kNotWrapped;
- foundSlot = false;
-
- /* Search for a slot until an appropriate one is found */
- while (!foundSlot)
- {
- /* See if a slot has <= maximum number of allowed collisions */
- collCount = 0;
- testWindow = FrontWindow();
- while (testWindow != nil && collCount <= maxCollCount)
- {
- /* Get the global rectangle covering entire window frame */
- testWindowRect = (**((WindowPeek)testWindow)->strucRgn).rgnBBox;
-
- /* If top left of window frame in testWindowRect, then collision */
- if (PtInRect( topLeft( testWindowRect ), &collRect ))
- collCount++;
-
- /* Go to the next window */
- testWindow = (WindowPtr)((WindowPeek) testWindow)->nextWindow;
- }
-
- /* If too many collisions, then shift collision rect to the next slot */
- if (collCount > maxCollCount)
- {
- /* Shift collision rectangle to the next slot */
- OffsetRect( /*◊*/&collRect, kCollWidth / 2, kCollHeight / 2 );
- OffsetRect( /*◊*/&workWindowRect, kCollWidth / 2, kCollHeight / 2 );
- if (workWindowRect.bottom > baseRect->bottom)
- {
- if (wrapStatus != kHorzWrapped)
- {
- /* Wrap collision rect vertically */
- collRect.top = initCollRect.top;
- collRect.bottom = initCollRect.bottom;
- wrapStatus = kVertWrapped;
- }
- else
- {
- /* Wrapped horz, try from start & allow 1 more collision */
- collRect = initCollRect;
- maxCollCount++;
- }
-
- /* Make workWindowRect follow collRect */
- workWindowRect.top = collRect.top + vBias + kCollHeight / 2;
- workWindowRect.left = collRect.left + hBias + kCollWidth / 2;
- workWindowRect.bottom = workWindowRect.top + windowRectHeight;
- workWindowRect.right = workWindowRect.left + windowRectWidth;
- }
- if (workWindowRect.right > baseRect->right)
- {
- if (wrapStatus != kVertWrapped)
- {
- /* Wrap collision rect horizontally */
- collRect.left = initCollRect.left;
- collRect.right = initCollRect.right;
- wrapStatus = kHorzWrapped;
- }
- else
- {
- /* Wrapped vert, try from start & allow 1 more collision */
- collRect = initCollRect;
- maxCollCount++;
- }
-
- /* Make workWindowRect follow collRect */
- workWindowRect.top = collRect.top + vBias + kCollHeight / 2;
- workWindowRect.left = collRect.left + hBias + kCollWidth / 2;
- workWindowRect.bottom = workWindowRect.top + windowRectHeight;
- workWindowRect.right = workWindowRect.left + windowRectWidth;
- }
- }
- else
- foundSlot = true;
- }
-
- /* Return the top-left corner of workWindowRect */
- OffsetRect( /*◊*/modWindowRect, workWindowRect.left - modWindowRect->left,
- workWindowRect.top - modWindowRect->top );
- }
-
-
- /******************************************************************************\
- * Private: GetWindowGDevice - Get GDevice that contains most of a window
- *
- * This routine searches through all active screen GDevices in the GDevice list
- * for the GDevice of the screen that has the greatest area of intersection with
- * the portRect of the window specified by theWindow. A handle to this GDevice
- * is returned. If the portRect of theWindow is doesn’t intersect any active
- * screen, then nil is returned.
- *
- * This routine can only be called if Color QuickDraw is implemented because
- * the Graphics Device Manager only exists on Color QuickDraw machines.
- \******************************************************************************/
-
- static GDHandle GetWindowGDevice(
- WindowPtr theWindow) /* Pointer to window being tested */
- {
- GDHandle testGDevice; /* Handle to GDevice being tested */
- GDHandle maxGDevice; /* GDevice with maximum intersection area */
- Rect windowRect; /* Rect of window’s portRect in global coords */
- Rect commonRect; /* Rect of intersection between windowRect & gdRect */
- long maxArea; /* Max area of GDevice/portRect intersection found */
- long commonArea; /* Area of intersection between windowRect & gdRect */
-
- /* gdRects in global coords, so get window’s portRect in global coords */
- GetGlobalWindowRect( theWindow, /*<*/&windowRect );
-
- /* Loop through all active screen GDevices */
- testGDevice = GetDeviceList();
- maxArea = 0;
- maxGDevice = nil;
- while (testGDevice != nil)
- {
- /* Only test if GDevice is active screen GDevice */
- if (TestDeviceAttribute( testGDevice, screenDevice ) &&
- TestDeviceAttribute ( testGDevice, screenActive ))
- /* Only check area if window and gdRect intersect */
- if (SectRect( &(**testGDevice).gdRect, &windowRect,
- /*<*/&commonRect ))
- {
- /* Find area common to GDevice.gdRect and window’s portRect */
- commonArea = (long)(commonRect.right - commonRect.left) *
- (commonRect.bottom - commonRect.top);
-
- /* If area > max area found, then update maxArea & maxGDevice */
- if (commonArea > maxArea)
- {
- maxArea = commonArea;
- maxGDevice = testGDevice;
- }
- }
-
- /* Try the next GDevice */
- testGDevice = GetNextDevice( testGDevice );
- }
-
- return maxGDevice;
- }
-
-
- /******************************************************************************\
- * Private: GetGlobalWindowRect - Get a window’s portRect in global coordinates
- *
- * The portRect of the window specified by theWindow is converted from the
- * window’s local coordinates to global (screen) coordinates. This converted
- * rectangle is returned in globalRect. If theWindow is nil, then globalRect
- * returns the rectangle [T:0 L:0 B:0 R:0].
- \******************************************************************************/
-
- static void GetGlobalWindowRect(
- WindowPtr theWindow, /* Pointer to window whose global rect we want */
- Rect *globalRect) /* Returns theWindow’s portRect in global coords */
- {
- GrafPtr savedPort; /* Pointer to current GrafPort; for restoring */
-
- if (theWindow != nil)
- {
- GetPort( /*<*/&savedPort );
- SetPort( theWindow );
- *globalRect = theWindow->portRect;
- LocalToGlobal( /*◊*/&topLeft( *globalRect ) );
- LocalToGlobal( /*◊*/&botRight( *globalRect ) );
- SetPort( savedPort );
- }
- else
- SetRect( /*<*/globalRect, 0, 0, 0, 0 );
- }
-
-
- #pragma segment Window Position
- /******************************************************************************\
- * Public: CalcWindowBias
- *
- * The only reliable way to calculate the bias of a window is actually to create
- * a visible window and measure the resulting thickness of the window frame, and
- * then to dispose of the window immediately after the measurement is taken. To
- * avoid visual threats against good taste, the window is created far outside of
- * any possible screen position, and it’s created behind any other windows to
- * avoid deactivating existing windows.
- *
- * To avoid as much heap disruption as possible, the window record is pre-
- * allocated as a handle, which is then locked. Then, NewWindow is called to
- * create the ephemeral window. The bias is then calculated by subtracting the
- * left and top coordinates of the structure region from the left and top
- * coordinates of the content region.
- \******************************************************************************/
-
- Point CalcWindowBias(
- short procID, /* Defproc ID of window whose bias is being calculated */
- Boolean goAwayFlag) /* True if window has a go-away box */
- {
- Point bias; /* Calculated bias */
- WindowPeek biasWindow; /* Pointer to temp window used for bias calc */
- Handle windowStore; /* Handle to window record storage (kept locked) */
- Rect windowRect; /* Rectangle of window in global coords */
- GrafPtr savedPort; /* Pointer to current GrafPort for restoring */
-
- /* In case an error happens, default to zero bias */
- bias.h = bias.v = 0;
-
- /* Allocate WindowRecord as relocatable to avoid cluttering heap */
- windowStore = NewHandle( sizeof (WindowRecord) );
- if (windowStore != nil)
- {
- GetPort( /*<*/&savedPort );
-
- /* WindowRecord must be locked */
- HLock( windowStore );
-
- /* Calc rectangle that’s almost certain to be off any screen */
- SetRect( /*<*/&windowRect, kOffScreenLeft, kOffScreenTop,
- kOffScreenRight, kOffScreenBottom );
-
- /* Create visible temporary window "behind" all existing windows */
- biasWindow = (WindowPeek)NewWindow( *windowStore, &windowRect, "\P",
- true, procID, nil, goAwayFlag, 0L );
- if (biasWindow != nil)
- {
- /* Bias is content region top-left - structure region top-left */
- bias.h = (**biasWindow->contRgn).rgnBBox.left - (**biasWindow->
- strucRgn).rgnBBox.left;
- bias.v = (**biasWindow->contRgn).rgnBBox.top - (**biasWindow->
- strucRgn).rgnBBox.top;
- }
-
- /* Pretend this never happened */
- SetPort( savedPort );
- CloseWindow( (WindowPtr)biasWindow );
- DisposHandle( windowStore );
- }
-
- return bias;
- }
-